package communicationModule;

import generalObjects.GroupView;
import generalObjects.LayerAccess;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.event.ChangeListener;

/**
 * Layer class responsible for multicasting messages from the GCom model. An
 * object of this class receives a batch of messages that should be multicasted
 * and then delegates the work to separate workerthreads provided by
 * {@link communicationModule.Multicast}.
 * 
 */
public class CMLayerAccess implements LayerAccess {

    private final Multicast multicaster;
    private final ReentrantLock lock;
    private List<SendResult> deliverbuf;
    private final ConcurrentHashMap<Long, Integer> received;
    private final ChangeListener receivedFilterUpdater;

    public CMLayerAccess(final Properties sessionProp, final GroupView gView,
            ConcurrentHashMap<Long, Integer> received,
            ChangeListener receivedFilterUpdater) {
        final boolean reliable = sessionProp.getProperty("multicast").equals(
                "true");
        lock = new ReentrantLock();
        deliverbuf = new ArrayList<SendResult>();
        multicaster = new Multicast(sessionProp.getProperty("nickname"),
                reliable, lock, gView);
        multicaster.setDeliverbuffer(deliverbuf);
        this.received = received;
        this.receivedFilterUpdater = receivedFilterUpdater;

    }

    @Override
    public List<SendResult> process(final List<MessageObject> mOList) {

        final ArrayList<MessageObject> process = new ArrayList<MessageObject>();
        for (final MessageObject msg : mOList) {
            final long id = msg.getId();
            if (!received.containsKey(id)) {
                process.add(msg);
                received.put(id, 0);
            } else {
                int nr = received.get(id) + 1;
                received.put(id, nr);
            }
        }
        if (receivedFilterUpdater != null && !mOList.isEmpty()) {
            receivedFilterUpdater.stateChanged(null);
        }

        if (process != null && !process.isEmpty()) {
            multicaster.send(process);
        }

        lock.lock();
        final List<SendResult> results = deliverbuf;
        deliverbuf = new ArrayList<SendResult>();
        multicaster.setDeliverbuffer(deliverbuf);
        lock.unlock();

        return results;
    }

    @Override
    public void setLowerLayer(final LayerAccess layer) {
        // not needed.
    }

    public Vector<String> getReceivedBufferStrings() {

        final Vector<String> result = new Vector<String>();

        for (final Entry<Long, Integer> e : received.entrySet()) {
            result.add("MsgID: " + e.getKey() + " Received duplicates: "
                    + e.getValue());
        }
        return result;
    }
}
